//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package project_doc_api_serv

import (
	"context"
	"time"

	"atomgit.com/openlinksaas/api-server/api_common"
	"atomgit.com/openlinksaas/api-server/dao"
	"atomgit.com/openlinksaas/api-server/dao/project_doc_dao"
	"atomgit.com/openlinksaas/api-server/dao/project_entry_dao"
	"atomgit.com/openlinksaas/api-server/dao/user_dao"
	"atomgit.com/openlinksaas/api-server/utils"
	"atomgit.com/openlinksaas/proto-gen-go.git/project_doc_api"
	"atomgit.com/openlinksaas/proto-gen-go.git/project_entry_api"
	"go.mongodb.org/mongo-driver/mongo"
)

// 列出文档历史版本
func (apiImpl *ProjectDocApiImpl) ListDocHistory(ctx context.Context, req *project_doc_api.ListDocHistoryRequest) (*project_doc_api.ListDocHistoryResponse, error) {
	emptyList := []*project_doc_api.DocHistory{}
	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_doc_api.ListDocHistoryResponse{
			Code:        project_doc_api.ListDocHistoryResponse_CODE_WRONG_SESSION,
			ErrMsg:      "会话不存在",
			HistoryList: emptyList,
		}, nil
	}
	//检查权限
	_, _, _, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_doc_api.ListDocHistoryResponse{
			Code:        project_doc_api.ListDocHistoryResponse_CODE_NO_PROJECT,
			ErrMsg:      "项目不存在",
			HistoryList: emptyList,
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_doc_api.ListDocHistoryResponse{
			Code:        project_doc_api.ListDocHistoryResponse_CODE_NO_PERMISSION,
			ErrMsg:      "没有权限",
			HistoryList: emptyList,
		}, nil
	} else if err != nil {
		return nil, err
	}

	entryItem, err := project_entry_dao.EntryDao.Get(ctx, req.DocId, req.ProjectId)
	if err != nil {
		return &project_doc_api.ListDocHistoryResponse{
			Code:   project_doc_api.ListDocHistoryResponse_CODE_NO_DOC,
			ErrMsg: "文档不存在",
		}, nil
	}
	if entryItem.EntryType != uint32(project_entry_api.ENTRY_TYPE_ENTRY_TYPE_DOC) {
		return &project_doc_api.ListDocHistoryResponse{
			Code:   project_doc_api.ListDocHistoryResponse_CODE_NO_DOC,
			ErrMsg: "文档不存在",
		}, nil
	}

	//读取最后10条历史
	itemList, err := project_doc_dao.ContentHistoryDao.ListLastTimeStamp(ctx, req.DocId, 10)
	if err != nil {
		return nil, err
	}

	//补充用户信息
	userIdList := []string{}
	for _, item := range itemList {
		userIdList = append(userIdList, item.UpdateUserId)
	}
	userItemList, err := user_dao.UserInfoDao.ListById(ctx, userIdList)
	if err != nil {
		return nil, err
	}
	userItemMap := map[string]*user_dao.UserInfoDbItem{}
	for _, userItem := range userItemList {
		userItemMap[userItem.UserId] = userItem
	}

	historyList := []*project_doc_api.DocHistory{}
	for _, item := range itemList {
		historyItem := &project_doc_api.DocHistory{
			HistoryId:    item.HistoryId,
			TimeStamp:    item.TimeStamp,
			UpdateUserId: item.UpdateUserId,
		}
		userItem, ok := userItemMap[item.UpdateUserId]
		if ok {
			historyItem.UpdateDisplayName = userItem.BasicInfo.DisplayName
			historyItem.UpdateLogoUri = userItem.BasicInfo.LogoUri
		}
		historyList = append(historyList, historyItem)
	}
	return &project_doc_api.ListDocHistoryResponse{
		Code:        project_doc_api.ListDocHistoryResponse_CODE_OK,
		HistoryList: historyList,
	}, nil
}

// 恢复文档到历史版本
func (apiImpl *ProjectDocApiImpl) GetDocInHistory(ctx context.Context, req *project_doc_api.GetDocInHistoryRequest) (*project_doc_api.GetDocInHistoryResponse, error) {
	emptyDoc := &project_doc_api.Doc{
		BaseInfo: &project_doc_api.BaseDoc{},
	}
	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_doc_api.GetDocInHistoryResponse{
			Code:   project_doc_api.GetDocInHistoryResponse_CODE_WRONG_SESSION,
			ErrMsg: "会话不存在",
			Doc:    emptyDoc,
		}, nil
	}
	//检查权限
	_, _, _, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_doc_api.GetDocInHistoryResponse{
			Code:   project_doc_api.GetDocInHistoryResponse_CODE_NO_PROJECT,
			ErrMsg: "项目不存在",
			Doc:    emptyDoc,
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_doc_api.GetDocInHistoryResponse{
			Code:   project_doc_api.GetDocInHistoryResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
			Doc:    emptyDoc,
		}, nil
	} else if err != nil {
		return nil, err
	}

	entryItem, err := project_entry_dao.EntryDao.Get(ctx, req.DocId, req.ProjectId)
	if err != nil {
		return &project_doc_api.GetDocInHistoryResponse{
			Code:   project_doc_api.GetDocInHistoryResponse_CODE_NO_DOC,
			ErrMsg: "文档不存在",
		}, nil
	}
	if entryItem.EntryType != uint32(project_entry_api.ENTRY_TYPE_ENTRY_TYPE_DOC) {
		return &project_doc_api.GetDocInHistoryResponse{
			Code:   project_doc_api.GetDocInHistoryResponse_CODE_NO_DOC,
			ErrMsg: "文档不存在",
		}, nil
	}

	//读取历史内容
	historyItem, err := project_doc_dao.ContentHistoryDao.Get(ctx, req.HistoryId, req.DocId)
	if err != nil {
		return &project_doc_api.GetDocInHistoryResponse{
			Code:   project_doc_api.GetDocInHistoryResponse_CODE_NO_DOC_HISTORY,
			ErrMsg: "没有文档记录",
			Doc:    emptyDoc,
		}, nil
	}

	return &project_doc_api.GetDocInHistoryResponse{
		Code: project_doc_api.GetDocInHistoryResponse_CODE_OK,
		Doc: &project_doc_api.Doc{
			DocId: historyItem.DocId,
			BaseInfo: &project_doc_api.BaseDoc{
				Content: historyItem.Content,
			},
		},
	}, nil
}

func (apiImpl *ProjectDocApiImpl) RecoverDocInHistory(ctx context.Context, req *project_doc_api.RecoverDocInHistoryRequest) (*project_doc_api.RecoverDocInHistoryResponse, error) {
	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_doc_api.RecoverDocInHistoryResponse{
			Code:   project_doc_api.RecoverDocInHistoryResponse_CODE_WRONG_SESSION,
			ErrMsg: "会话不存在",
		}, nil
	}
	//检查权限
	projectItem, _, roleItem, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_doc_api.RecoverDocInHistoryResponse{
			Code:   project_doc_api.RecoverDocInHistoryResponse_CODE_NO_PROJECT,
			ErrMsg: "项目不存在",
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_doc_api.RecoverDocInHistoryResponse{
			Code:   project_doc_api.RecoverDocInHistoryResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	} else if err != nil {
		return nil, err
	}

	entryItem, err := project_entry_dao.EntryDao.Get(ctx, req.DocId, req.ProjectId)
	if err != nil {
		return &project_doc_api.RecoverDocInHistoryResponse{
			Code:   project_doc_api.RecoverDocInHistoryResponse_CODE_NO_DOC,
			ErrMsg: "文档不存在",
		}, nil
	}
	if entryItem.EntryType != uint32(project_entry_api.ENTRY_TYPE_ENTRY_TYPE_DOC) {
		return &project_doc_api.RecoverDocInHistoryResponse{
			Code:   project_doc_api.RecoverDocInHistoryResponse_CODE_NO_DOC,
			ErrMsg: "文档不存在",
		}, nil
	}

	canUpdate := api_common.CheckEntryCanUpdate(sessionItem, projectItem, roleItem, entryItem)

	if !canUpdate {
		return &project_doc_api.RecoverDocInHistoryResponse{
			Code:   project_doc_api.RecoverDocInHistoryResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	}
	//检查编辑状态
	inEdit, err := dao.CacheDao.ExistDocEdit(ctx, req.DocId)
	if err != nil {
		return nil, err
	}
	if inEdit {
		return &project_doc_api.RecoverDocInHistoryResponse{
			Code:   project_doc_api.RecoverDocInHistoryResponse_CODE_IN_EDIT,
			ErrMsg: "文档正在被编辑",
		}, nil
	}
	//读取历史内容
	historyItem, err := project_doc_dao.ContentHistoryDao.Get(ctx, req.HistoryId, req.DocId)
	if err != nil {
		return &project_doc_api.RecoverDocInHistoryResponse{
			Code:   project_doc_api.RecoverDocInHistoryResponse_CODE_NO_DOC_HISTORY,
			ErrMsg: "没有文档记录",
		}, nil
	}
	contentItem, err := project_doc_dao.DocContentDao.Get(ctx, req.DocId)
	if err != nil {
		return nil, err
	}
	if contentItem.Content == historyItem.Content {
		return &project_doc_api.RecoverDocInHistoryResponse{
			Code: project_doc_api.RecoverDocInHistoryResponse_CODE_OK,
		}, nil
	}

	nowTime := time.Now().UnixNano() / 1e6
	dbSess, err := dao.StartSession()
	if err != nil {
		return nil, err
	}
	defer dao.EndSession(dbSess)

	ret, err := dbSess.WithTransaction(ctx, func(sessCtx mongo.SessionContext) (interface{}, error) {
		err = project_doc_dao.DocContentDao.Update(sessCtx, req.DocId, historyItem.Content)
		if err != nil {
			return nil, err
		}
		err = project_entry_dao.EntryDao.SetUpdateInfo(sessCtx, req.DocId, req.ProjectId, sessionItem.UserId, nowTime)
		if err != nil {
			return nil, err
		}
		err = project_doc_dao.ContentHistoryDao.Insert(sessCtx, &project_doc_dao.ContentHistoryDbItem{
			DocId:        req.DocId,
			TimeStamp:    nowTime,
			Content:      historyItem.Content,
			UpdateUserId: sessionItem.UserId,
		})
		if err != nil {
			return nil, err
		}
		err = apiImpl.removeUnNeedHistory(sessCtx, req.DocId)
		if err != nil {
			return nil, err
		}
		return &project_doc_api.RecoverDocInHistoryResponse{
			Code: project_doc_api.RecoverDocInHistoryResponse_CODE_OK,
		}, nil
	})
	if err != nil {
		return nil, err
	}
	return ret.(*project_doc_api.RecoverDocInHistoryResponse), nil
}

func (apiImpl *ProjectDocApiImpl) removeUnNeedHistory(ctx context.Context, docId string) error {
	itemList, err := project_doc_dao.ContentHistoryDao.ListLastTimeStamp(ctx, docId, 10)
	if err != nil {
		return err
	}
	if len(itemList) <= 1 {
		return nil
	}
	lastTimeStamp := itemList[0].TimeStamp
	removeTsList := []int64{}
	for _, item := range itemList[1:] {
		if lastTimeStamp-item.TimeStamp < 300*1000 {
			removeTsList = append(removeTsList, item.TimeStamp)
		}
	}
	if len(removeTsList) > 0 {
		err = project_doc_dao.ContentHistoryDao.RemoveByTimeStamp(ctx, docId, removeTsList)
		if err != nil {
			return err
		}
	}
	return nil
}
